﻿using System;
using System.ComponentModel;
using System.Drawing.Design;
using System.Security.Permissions;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Xml.Linq;

namespace SPAdaptiveMenu.Controls
{
    /// <summary>
    /// Represents the container for the menu structure. 
    /// A menu is composed of <see cref="MenuItem"/> objects that represent the individual menu commands in the menu structure.
    /// </summary>
    [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal),
     AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal),
     DefaultProperty("ItemCollection"),
     ParseChildren(true, "ItemCollection"),
     ToolboxData("<{0}:Menu runat=\"server\"> </{0}:Menu>")]
    [Serializable]
    public class Menu : HierarchicalDataBoundControl, ICallbackEventHandler
    {
        /// <summary>
        /// Title of menu
        /// </summary>
        public string Title { get; set; }
        /// <summary>
        /// Size of icons
        /// </summary>
        public MenuIconSize IconSize { get; set; }

        //public bool IsAsync { get; set; }

        ///<summary>
        /// Collection of items
        ///</summary>
        [Category("Behavior"),
         Description("The menu items collection"),
         DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
         Editor(typeof(MenuItemCollectionEditor), typeof(UITypeEditor)),
         PersistenceMode(PersistenceMode.InnerProperty)]
        public MenuItemCollection ItemCollection
        {
            get { return _itemCollection ?? (_itemCollection = new MenuItemCollection()); }
            set { _itemCollection = value; }
        }
        private MenuItemCollection _itemCollection;

        ///<summary>
        ///</summary>
        ///<param name="page"></param>
        ///<returns></returns>
        public static string ScriptUrl(Page page)
        {
            var csm = page.ClientScript;
            if (csm == null)
                throw new NullReferenceException("ClientScript is empty");
            var url = csm.GetWebResourceUrl(typeof(Menu), "SPAdaptiveMenu.SPMenu.js");
            //var cssUrl = csm.GetWebResourceUrl(typeof(Menu), "SPAdaptiveMenu.SPMenu.css");
            return url;
        }

        ///<summary>
        ///</summary>
        ///<param name="page"></param>
        ///<returns></returns>
        public static string CssUrl(Page page)
        {
            var csm = page.ClientScript;
            if (csm == null)
                throw new NullReferenceException("ClientScript is empty");
            var url = csm.GetWebResourceUrl(typeof(Menu), "SPAdaptiveMenu.SPMenu.css");
            return url;
        }

        /// <summary>
        /// </summary>
        public Menu()
        {
            _itemCollection = new MenuItemCollection();
            //IsAsync = true;
        }

        protected override void AddParsedSubObject(object obj)
        {
            if (!(obj is MenuItem)) return;
            ItemCollection.Add((MenuItem)obj);
            return;
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            Page.RegisterRequiresControlState(this);
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            var csm = Page.ClientScript;
            var url = csm.GetWebResourceUrl(GetType(), "SPAdaptiveMenu.SPMenu.js");
            var cssUrl = csm.GetWebResourceUrl(GetType(), "SPAdaptiveMenu.SPMenu.css");
            if (!csm.IsClientScriptIncludeRegistered("SPMenu"))
            {
                csm.RegisterClientScriptInclude("SPMenu", url);
                var css = new HtmlLink { Href = cssUrl };
                css.Attributes["rel"] = "stylesheet";
                css.Attributes["type"] = "text/css";
                css.Attributes["media"] = "all";
                Page.Header.Controls.Add(css);
            }
            const string callbackScript = @"function GetSPMenu(arg, context) {{WebForm_DoCallback(arg,null,SPMenuInit,context,null,false); }}";
            csm.RegisterClientScriptBlock(GetType(), "GetSPMenu", callbackScript, true);

        }

        protected override void CreateChildControls()
        {
            base.CreateChildControls();
            foreach (MenuItem item in ItemCollection)
            {
                Controls.Add(item);
            }
        }

        protected override void OnDataBinding(EventArgs e)
        {
            //if(DataSource != null)
            //    DataBindNode(((IHierarchicalDataSource)DataSource).GetHierarchicalView());
            base.OnDataBinding(e);
        }

        protected override void OnDataBound(EventArgs e)
        {
            base.OnDataBound(e);
        }

        private void DataBindNode(SiteMapNode node)
        {

        }

        protected override void Render(HtmlTextWriter writer)
        {
            writer.Write(AnchorHTML);
            //if (!IsAsync)
            //{
            writer.Write(MenuBeginHTML);
            RenderChildren(writer);
            writer.Write(MenuEndHTML);
            //}
        }

        protected override object SaveControlState()
        {
            base.SaveControlState();
            var obj = base.SaveControlState();
            return obj != null
                ? (object)new Pair(obj, Serialized.ToString())
                : Serialized.ToString();
        }

        protected override void LoadControlState(object state)
        {
            if (state == null) return;
            var p = state as Pair;
            if (p != null)
            {
                base.LoadControlState(p.First);
                InitProperties(XElement.Parse((string)p.Second));

            }
            else
            {
                if (state is string)
                    InitProperties(XElement.Parse((string)state));
                else
                    base.LoadControlState(state);
            }
        }

        private void InitProperties(XElement element)
        {
            IconSize = element.Attribute("IconSize").Value == "Small"
                               ? MenuIconSize.Small
                               : MenuIconSize.Large;
            Title = element.Attribute("Title").Value;
        }

        private XElement Serialized
        {
            get
            {
                var xml = new XElement("Menu",
                                       new XAttribute("Title", Title),
                                       new XAttribute("IconSize", IconSize));
                foreach (MenuItem item in ItemCollection)
                {
                    xml.Add(item.Serialized);
                }
                return xml;
            }
        }

        private string AnchorHTML
        {
            get
            {
                var onclick = false//IsAsync
                                  ? string.Format(
                                      "javascript: GetSPMenu('{0}','{1}')",
                                      UniqueID,
                                      ClientID)
                                  : string.Format(
                                      "javascript:OpenWebPartMenu('{0}_menu', document.getElementById('{0}_Container'), 'False'); TrapMenuClick(event); return false;",
                                      ClientID);
                var anchorHTML = string.Format(
                    @"<div class=""sp-menu"" style=""width:{3}"" id=""{0}_Container"" onclick=""{2}"">
                        <div class=""sp-menu-label"">{1}</div>
                        <div class=""sp-menu-anchor"" onmouseout=""this.className='sp-menu-anchor'"" onmouseover=""this.className='sp-menu-anchor-hover'""
                            <span>&nbsp;</span>
                            <a onclick=""{2}"" href=""javascript:;""><img style=""visibility: hidden;"" src=""/_layouts/images/ecbarw.png""></a>
                            <span>&nbsp;</span>
                        </div>
                    </div>",
                    ClientID,
                    Title,
                    onclick,
                    Width);
                return anchorHTML;
            }
        }

        private string MenuBeginHTML
        {
            get
            {
                var largeSizeFlagStr = IconSize == MenuIconSize.Large ? "true" : "false";
                return string.Format(@"<menu id=""{0}_menu"" type=""ServerMenu"" largeiconmode=""{1}"" class=""ms-SrvMenuUI"">", ClientID, largeSizeFlagStr);
            }
        }

        private static string MenuEndHTML
        {
            get
            {
                return @"</menu>";
            }
        }

        #region Implementation of ICallbackEventHandler

        public void RaiseCallbackEvent(string eventArgument)
        {

        }

        public string GetCallbackResult()
        {
            return HTML;
        }

        #endregion

        private string HTML
        {
            get
            {
                var sb = new StringBuilder(10);
                sb.Append(MenuBeginHTML);
                foreach (MenuItem item in ItemCollection)
                {
                    sb.Append(item.HTML.ToString());
                }
                sb.Append(MenuEndHTML);
                return sb.ToString()
                    .Replace("<menuitem", "<ie:menuitem")
                    .Replace("</menuitem>", "</ie:menuitem>");
            }
        }
    }
}
